/*---------------------------------------------------------------------------
 
  FILENAME:
        datafeed.c
 
  PURPOSE:
        Define the datafeed API.
 
  REVISION HISTORY:
        Date            Engineer        Revision        Remarks
        12/16/2009      M.S. Teel       0               Original
 
  NOTES:
        
 
  LICENSE:
        Copyright (c) 2009, Mark S. Teel (mark@teel.ws)
  
        This source code is released for free distribution under the terms 
        of the GNU General Public License.
  
----------------------------------------------------------------------------*/

//  ... System header files
#include <errno.h>


//  ... Local header files
#define DATAFEED_INSTANTIATE        TRUE
#include <datafeed.h>


//  ... methods

static USHORT swapShortNTOH(UCHAR* item)
{
    USHORT*     sh_item = (USHORT*)item;
    USHORT      retVal;

    retVal = ntohs(*sh_item);
    return retVal;
}

static USHORT swapShortHTON(UCHAR* item)
{
    USHORT*     sh_item = (USHORT*)item;
    USHORT      retVal;

    retVal = htons(*sh_item);
    return retVal;
}

static int ReadExact(RADSOCK_ID socket, void *bfr, int len, ULONG msTimeout)
{
    int             rval, index = 0;
    ULONG           cumTime = 0;
    ULONGLONG       readTime;
    UCHAR           *ptr = (UCHAR *)bfr;

    while (index < len && cumTime < msTimeout)
    {
        readTime = radTimeGetMSSinceEpoch ();
        rval = radSocketReadExact(socket, &ptr[index], len - index);
        if (rval < 0)
        {
            if (errno != EINTR && errno != EAGAIN)
            {
                radMsgLog (PRI_HIGH, "ReadExact ERROR: %s", strerror(errno));
                return ERROR;
            }
        }
        else
        {
            index += rval;
        }

        readTime = radTimeGetMSSinceEpoch () - readTime;
        cumTime += (ULONG)readTime;
        if (index < len && cumTime < msTimeout)
        {
            readTime = radTimeGetMSSinceEpoch ();
            radUtilsSleep (9);
            readTime = radTimeGetMSSinceEpoch () - readTime;
            cumTime += (ULONG)readTime;
        }
    }

    return ((index == len) ? len : 0);
}

int datafeedSyncStartOfFrame(RADSOCK_ID socket)
{
    USHORT          start;
    int             retVal;

    retVal = ReadExact(socket, (void *)&start, sizeof (USHORT), DF_WAIT_FIRST);
    if (retVal == ERROR)
    {
        radMsgLog (PRI_HIGH, "datafeed: socket read 1 error - abort!");
        return ERROR;
    }
    else if (retVal == 0)
    {
        return ERROR_ABORT;
    }
    else if (retVal != sizeof(USHORT))
    {
        return FALSE;
    }
    else if (start != DF_LOOP_START_FRAME[0])
    {
        return FALSE;
    }

    if (ReadExact(socket, (void *)&start, sizeof (USHORT), DF_WAIT_MORE) 
        != sizeof (USHORT))
    {
        radMsgLog (PRI_HIGH, "datafeed: socket read 2 error - abort!");
        return ERROR;
    }
    else if (start != DF_LOOP_START_FRAME[1])
    {
        return FALSE;
    }
 
    if (ReadExact(socket, (void *)&start, sizeof (USHORT), DF_WAIT_MORE) 
        != sizeof (USHORT))
    {
        radMsgLog (PRI_HIGH, "datafeed: socket read 3 error - abort!");
        return ERROR;
    }
    else if (start != DF_LOOP_START_FRAME[2])
    {
        return FALSE;
    }
    
    if (ReadExact(socket, (void *)&start, sizeof (USHORT), DF_WAIT_MORE) 
        != sizeof (USHORT))
    {
        radMsgLog (PRI_HIGH, "datafeed: socket read 4 error - abort!");
        return ERROR;
    }
    else if (start == DF_LOOP_START_FRAME[3])
    {
        return (int)DF_LOOP_START_FRAME[3];
    }
    else if (start == DF_ARCHIVE_START_FRAME[3])
    {
        return (int)DF_ARCHIVE_START_FRAME[3];
    }
    else if (start == DF_RQST_ARCHIVE_START_FRAME[3])
    {
        return (int)DF_RQST_ARCHIVE_START_FRAME[3];
    }

    return FALSE;
}


int datafeedConvertLOOP_HTON(LOOP_PKT_FP* dest, LOOP_PKT* src)
{
    USHORT*         tempshort;

    dest->barometer                     = (ULONG)(src->barometer * DF_FP_SHIFT);
    dest->barometer                     = htonl(dest->barometer);

    dest->stationPressure               = (ULONG)(src->stationPressure * DF_FP_SHIFT);
    dest->stationPressure               = htonl(dest->stationPressure);

    dest->altimeter                     = (ULONG)(src->altimeter * DF_FP_SHIFT);
    dest->altimeter                     = htonl(dest->altimeter);

    dest->inTemp                        = (ULONG)(src->inTemp * DF_FP_SHIFT);
    dest->inTemp                        = htonl(dest->inTemp);

    dest->outTemp                       = (ULONG)(src->outTemp * DF_FP_SHIFT);
    dest->outTemp                       = htonl(dest->outTemp);

    dest->inHumidity                    = htons(src->inHumidity);
    dest->outHumidity                   = htons(src->outHumidity);
    dest->windSpeed                     = htons(src->windSpeed);
    dest->windDir                       = htons(src->windDir);
    dest->windGust                      = htons(src->windGust);
    dest->windGustDir                   = htons(src->windGustDir);

    dest->rainRate                      = (ULONG)(src->rainRate * DF_FP_SHIFT);
    dest->rainRate                      = htonl(dest->rainRate);

    dest->sampleRain                    = (ULONG)(src->sampleRain * DF_FP_SHIFT);
    dest->sampleRain                    = htonl(dest->sampleRain);

    dest->sampleET                      = (ULONG)(src->sampleET * DF_FP_SHIFT);
    dest->sampleET                      = htonl(dest->sampleET);

    dest->radiation                     = htons(src->radiation);

    dest->UV                            = (ULONG)(src->UV * DF_FP_SHIFT);
    dest->UV                            = htonl(dest->UV);

    dest->dewpoint                      = (ULONG)(src->dewpoint * DF_FP_SHIFT);
    dest->dewpoint                      = htonl(dest->dewpoint);

    dest->windchill                     = (ULONG)(src->windchill * DF_FP_SHIFT);
    dest->windchill                     = htonl(dest->windchill);

    dest->heatindex                     = (ULONG)(src->heatindex * DF_FP_SHIFT);
    dest->heatindex                     = htonl(dest->heatindex);

    dest->stormRain                     = (ULONG)(src->stormRain * DF_FP_SHIFT);
    dest->stormRain                     = htonl(dest->stormRain);

    dest->stormStart                    = (ULONG)(src->stormStart * DF_FP_SHIFT);
    dest->stormStart                    = htonl(dest->stormStart);

    dest->dayRain                       = (ULONG)(src->dayRain * DF_FP_SHIFT);
    dest->dayRain                       = htonl(dest->dayRain);

    dest->monthRain                     = (ULONG)(src->monthRain * DF_FP_SHIFT);
    dest->monthRain                     = htonl(dest->monthRain);

    dest->yearRain                      = (ULONG)(src->yearRain * DF_FP_SHIFT);
    dest->yearRain                      = htonl(dest->yearRain);

    dest->dayET                         = (ULONG)(src->dayET * DF_FP_SHIFT);
    dest->dayET                         = htonl(dest->dayET);

    dest->monthET                       = (ULONG)(src->monthET * DF_FP_SHIFT);
    dest->monthET                       = htonl(dest->monthET);

    dest->yearET                        = (ULONG)(src->yearET * DF_FP_SHIFT);
    dest->yearET                        = htonl(dest->yearET);

    dest->intervalAvgWCHILL             = (ULONG)(src->intervalAvgWCHILL * DF_FP_SHIFT);
    dest->intervalAvgWCHILL             = htonl(dest->intervalAvgWCHILL);

    dest->intervalAvgWSPEED             = (ULONG)(src->intervalAvgWSPEED * DF_FP_SHIFT);
    dest->intervalAvgWSPEED             = htonl(dest->intervalAvgWSPEED);

    dest->yearRainMonth                 = htons(src->yearRainMonth);
    dest->rxCheckPercent                = htons(src->rxCheckPercent);
    dest->tenMinuteAvgWindSpeed         = htons(src->tenMinuteAvgWindSpeed);
    dest->forecastIcon                  = htons(src->forecastIcon);
    dest->forecastRule                  = htons(src->forecastRule);
    dest->txBatteryStatus               = htons(src->txBatteryStatus);
    dest->yearRainMonth                 = htons(src->yearRainMonth);

    dest->consBatteryVoltage            = (ULONG)(src->consBatteryVoltage * DF_FP_SHIFT);
    dest->consBatteryVoltage            = htonl(dest->consBatteryVoltage);

    dest->extraTemp1                    = (ULONG)(src->extraTemp1 * DF_FP_SHIFT);
    dest->extraTemp1                    = htonl(dest->extraTemp1);

    dest->extraTemp2                    = (ULONG)(src->extraTemp2 * DF_FP_SHIFT);
    dest->extraTemp2                    = htonl(dest->extraTemp2);

    dest->extraTemp3                    = (ULONG)(src->extraTemp3 * DF_FP_SHIFT);
    dest->extraTemp3                    = htonl(dest->extraTemp3);

    dest->soilTemp1                     = (ULONG)(src->soilTemp1 * DF_FP_SHIFT);
    dest->soilTemp1                     = htonl(dest->soilTemp1);

    dest->soilTemp2                     = (ULONG)(src->soilTemp2 * DF_FP_SHIFT);
    dest->soilTemp2                     = htonl(dest->soilTemp2);

    dest->soilTemp3                     = (ULONG)(src->soilTemp3 * DF_FP_SHIFT);
    dest->soilTemp3                     = htonl(dest->soilTemp3);

    dest->soilTemp4                     = (ULONG)(src->soilTemp4 * DF_FP_SHIFT);
    dest->soilTemp4                     = htonl(dest->soilTemp4);

    dest->leafTemp1                     = (ULONG)(src->leafTemp1 * DF_FP_SHIFT);
    dest->leafTemp1                     = htonl(dest->leafTemp1);

    dest->leafTemp2                     = (ULONG)(src->leafTemp2 * DF_FP_SHIFT);
    dest->leafTemp2                     = htonl(dest->leafTemp2);

    tempshort = (USHORT*)&(dest->extraHumid1);
    *tempshort = swapShortHTON(&dest->extraHumid1);
    tempshort = (USHORT*)&(dest->soilMoist1);
    *tempshort = swapShortHTON(&dest->soilMoist1);
    tempshort = (USHORT*)&(dest->leafWet1);
    *tempshort = swapShortHTON(&dest->leafWet1);
    
    dest->wxt510Hail                    = (ULONG)(src->wxt510Hail * DF_FP_SHIFT);
    dest->wxt510Hail                    = htonl(dest->wxt510Hail);

    dest->wxt510Hailrate                = (ULONG)(src->wxt510Hailrate * DF_FP_SHIFT);
    dest->wxt510Hailrate                = htonl(dest->wxt510Hailrate);

    dest->wxt510HeatingTemp             = (ULONG)(src->wxt510HeatingTemp * DF_FP_SHIFT);
    dest->wxt510HeatingTemp             = htonl(dest->wxt510HeatingTemp);

    dest->wxt510HeatingVoltage          = (ULONG)(src->wxt510HeatingVoltage * DF_FP_SHIFT);
    dest->wxt510HeatingVoltage          = htonl(dest->wxt510HeatingVoltage);

    dest->wxt510SupplyVoltage           = (ULONG)(src->wxt510SupplyVoltage * DF_FP_SHIFT);
    dest->wxt510SupplyVoltage           = htonl(dest->wxt510SupplyVoltage);

    dest->wxt510ReferenceVoltage        = (ULONG)(src->wxt510ReferenceVoltage * DF_FP_SHIFT);
    dest->wxt510ReferenceVoltage        = htonl(dest->wxt510ReferenceVoltage);

    dest->wxt510RainDuration            = (ULONG)(src->wxt510RainDuration * DF_FP_SHIFT);
    dest->wxt510RainDuration            = htonl(dest->wxt510RainDuration);

    dest->wxt510RainPeakRate            = (ULONG)(src->wxt510RainPeakRate * DF_FP_SHIFT);
    dest->wxt510RainPeakRate            = htonl(dest->wxt510RainPeakRate);

    dest->wxt510HailDuration            = (ULONG)(src->wxt510HailDuration * DF_FP_SHIFT);
    dest->wxt510HailDuration            = htonl(dest->wxt510HailDuration);

    dest->wxt510HailPeakRate            = (ULONG)(src->wxt510HailPeakRate * DF_FP_SHIFT);
    dest->wxt510HailPeakRate            = htonl(dest->wxt510HailPeakRate);

    dest->wxt510Rain                    = (ULONG)(src->wxt510Rain * DF_FP_SHIFT);
    dest->wxt510Rain                    = htonl(dest->wxt510Rain);

    dest->wmr918Pool                    = (ULONG)(src->wmr918Pool * DF_FP_SHIFT);
    dest->wmr918Pool                    = htonl(dest->wmr918Pool);

    tempshort = (USHORT*)&(dest->wmr918Humid3);
    *tempshort = swapShortHTON(&dest->wmr918Humid3);
    tempshort = (USHORT*)&(dest->wmr918WindBatteryStatus);
    *tempshort = swapShortHTON(&dest->wmr918WindBatteryStatus);
    tempshort = (USHORT*)&(dest->wmr918OutTempBatteryStatus);
    *tempshort = swapShortHTON(&dest->wmr918OutTempBatteryStatus);
    tempshort = (USHORT*)&(dest->wmr918poolTempBatteryStatus);
    *tempshort = swapShortHTON(&dest->wmr918poolTempBatteryStatus);
    tempshort = (USHORT*)&(dest->wmr918extra2BatteryStatus);
    *tempshort = swapShortHTON(&dest->wmr918extra2BatteryStatus);

    return OK;
}

int datafeedConvertLOOP_NTOH(LOOP_PKT* dest, LOOP_PKT_FP* src)
{
    USHORT*         tempshort;

    src->barometer                      = ntohl(src->barometer);
    dest->barometer                     = (float)src->barometer/DF_FP_SHIFT;

    src->stationPressure                = ntohl(src->stationPressure);
    dest->stationPressure               = (float)src->stationPressure/DF_FP_SHIFT;

    src->altimeter                      = ntohl(src->altimeter);
    dest->altimeter                     = (float)src->altimeter/DF_FP_SHIFT;

    src->inTemp                         = ntohl(src->inTemp);
    dest->inTemp                        = (float)src->inTemp/DF_FP_SHIFT;

    src->outTemp                        = ntohl(src->outTemp);
    dest->outTemp                       = (float)src->outTemp/DF_FP_SHIFT;

    dest->inHumidity                    = ntohs(src->inHumidity);
    dest->outHumidity                   = ntohs(src->outHumidity);
    dest->windSpeed                     = ntohs(src->windSpeed);
    dest->windDir                       = ntohs(src->windDir);
    dest->windGust                      = ntohs(src->windGust);
    dest->windGustDir                   = ntohs(src->windGustDir);

    src->rainRate                       = ntohl(src->rainRate);
    dest->rainRate                      = (float)src->rainRate/DF_FP_SHIFT;

    src->sampleRain                     = ntohl(src->sampleRain);
    dest->sampleRain                    = (float)src->sampleRain/DF_FP_SHIFT;

    src->sampleET                       = ntohl(src->sampleET);
    dest->sampleET                      = (float)src->sampleET/DF_FP_SHIFT;

    dest->radiation                     = ntohs(src->radiation);

    src->UV                             = ntohl(src->UV);
    dest->UV                            = (float)src->UV/DF_FP_SHIFT;

    src->dewpoint                       = ntohl(src->dewpoint);
    dest->dewpoint                      = (float)src->dewpoint/DF_FP_SHIFT;

    src->windchill                      = ntohl(src->windchill);
    dest->windchill                     = (float)src->windchill/DF_FP_SHIFT;

    src->heatindex                      = ntohl(src->heatindex);
    dest->heatindex                     = (float)src->heatindex/DF_FP_SHIFT;

    src->stormRain                      = ntohl(src->stormRain);
    dest->stormRain                     = (float)src->stormRain/DF_FP_SHIFT;

    src->stormStart                     = ntohl(src->stormStart);
    dest->stormStart                    = (float)src->stormStart/DF_FP_SHIFT;

    src->dayRain                        = ntohl(src->dayRain);
    dest->dayRain                       = (float)src->dayRain/DF_FP_SHIFT;

    src->monthRain                      = ntohl(src->monthRain);
    dest->monthRain                     = (float)src->monthRain/DF_FP_SHIFT;

    src->yearRain                       = ntohl(src->yearRain);
    dest->yearRain                      = (float)src->yearRain/DF_FP_SHIFT;

    src->dayET                          = ntohl(src->dayET);
    dest->dayET                         = (float)src->dayET/DF_FP_SHIFT;

    src->monthET                        = ntohl(src->monthET);
    dest->monthET                       = (float)src->monthET/DF_FP_SHIFT;

    src->yearET                         = ntohl(src->yearET);
    dest->yearET                        = (float)src->yearET/DF_FP_SHIFT;

    src->intervalAvgWCHILL              = ntohl(src->intervalAvgWCHILL);
    dest->intervalAvgWCHILL             = (float)src->intervalAvgWCHILL/DF_FP_SHIFT;

    src->intervalAvgWSPEED              = ntohl(src->intervalAvgWSPEED);
    dest->intervalAvgWSPEED             = (float)src->intervalAvgWSPEED/DF_FP_SHIFT;

    dest->yearRainMonth                 = ntohs(src->yearRainMonth);
    dest->rxCheckPercent                = ntohs(src->rxCheckPercent);
    dest->tenMinuteAvgWindSpeed         = ntohs(src->tenMinuteAvgWindSpeed);
    dest->forecastIcon                  = ntohs(src->forecastIcon);
    dest->forecastRule                  = ntohs(src->forecastRule);
    dest->txBatteryStatus               = ntohs(src->txBatteryStatus);
    dest->yearRainMonth                 = ntohs(src->yearRainMonth);

    src->consBatteryVoltage             = ntohl(src->consBatteryVoltage);
    dest->consBatteryVoltage            = (float)src->consBatteryVoltage/DF_FP_SHIFT;

    src->extraTemp1                     = ntohl(src->extraTemp1);
    dest->extraTemp1                    = (float)src->extraTemp1/DF_FP_SHIFT;

    src->extraTemp2                     = ntohl(src->extraTemp2);
    dest->extraTemp2                    = (float)src->extraTemp2/DF_FP_SHIFT;

    src->extraTemp3                     = ntohl(src->extraTemp3);
    dest->extraTemp3                    = (float)src->extraTemp3/DF_FP_SHIFT;

    src->soilTemp1                      = ntohl(src->soilTemp1);
    dest->soilTemp1                     = (float)src->soilTemp1/DF_FP_SHIFT;

    src->soilTemp2                      = ntohl(src->soilTemp2);
    dest->soilTemp2                     = (float)src->soilTemp2/DF_FP_SHIFT;

    src->soilTemp3                      = ntohl(src->soilTemp3);
    dest->soilTemp3                     = (float)src->soilTemp3/DF_FP_SHIFT;

    src->soilTemp4                      = ntohl(src->soilTemp4);
    dest->soilTemp4                     = (float)src->soilTemp4/DF_FP_SHIFT;

    src->leafTemp1                      = ntohl(src->leafTemp1);
    dest->leafTemp1                     = (float)src->leafTemp1/DF_FP_SHIFT;

    src->leafTemp2                      = ntohl(src->leafTemp2);
    dest->leafTemp2                     = (float)src->leafTemp2/DF_FP_SHIFT;

    tempshort = (USHORT*)&(dest->extraHumid1);
    *tempshort = swapShortNTOH(&dest->extraHumid1);
    tempshort = (USHORT*)&(dest->soilMoist1);
    *tempshort = swapShortNTOH(&dest->soilMoist1);
    tempshort = (USHORT*)&(dest->leafWet1);
    *tempshort = swapShortNTOH(&dest->leafWet1);
    
    src->wxt510Hail                     = ntohl(src->wxt510Hail);
    dest->wxt510Hail                    = (float)src->wxt510Hail/DF_FP_SHIFT;

    src->wxt510Hailrate                 = ntohl(src->wxt510Hailrate);
    dest->wxt510Hailrate                = (float)src->wxt510Hailrate/DF_FP_SHIFT;

    src->wxt510HeatingTemp              = ntohl(src->wxt510HeatingTemp);
    dest->wxt510HeatingTemp             = (float)src->wxt510HeatingTemp/DF_FP_SHIFT;

    src->wxt510HeatingVoltage           = ntohl(src->wxt510HeatingVoltage);
    dest->wxt510HeatingVoltage          = (float)src->wxt510HeatingVoltage/DF_FP_SHIFT;

    src->wxt510SupplyVoltage            = ntohl(src->wxt510SupplyVoltage);
    dest->wxt510SupplyVoltage           = (float)src->wxt510SupplyVoltage/DF_FP_SHIFT;

    src->wxt510ReferenceVoltage         = ntohl(src->wxt510ReferenceVoltage);
    dest->wxt510ReferenceVoltage        = (float)src->wxt510ReferenceVoltage/DF_FP_SHIFT;

    src->wxt510RainDuration             = ntohl(src->wxt510RainDuration);
    dest->wxt510RainDuration            = (float)src->wxt510RainDuration/DF_FP_SHIFT;

    src->wxt510RainPeakRate             = ntohl(src->wxt510RainPeakRate);
    dest->wxt510RainPeakRate            = (float)src->wxt510RainPeakRate/DF_FP_SHIFT;

    src->wxt510HailDuration             = ntohl(src->wxt510HailDuration);
    dest->wxt510HailDuration            = (float)src->wxt510HailDuration/DF_FP_SHIFT;

    src->wxt510HailPeakRate             = ntohl(src->wxt510HailPeakRate);
    dest->wxt510HailPeakRate            = (float)src->wxt510HailPeakRate/DF_FP_SHIFT;

    src->wxt510Rain                     = ntohl(src->wxt510Rain);
    dest->wxt510Rain                    = (float)src->wxt510Rain/DF_FP_SHIFT;

    src->wmr918Pool                     = ntohl(src->wmr918Pool);
    dest->wmr918Pool                    = (float)src->wmr918Pool/DF_FP_SHIFT;

    tempshort = (USHORT*)&(dest->wmr918Humid3);
    *tempshort = swapShortNTOH(&dest->wmr918Humid3);
    tempshort = (USHORT*)&(dest->wmr918WindBatteryStatus);
    *tempshort = swapShortNTOH(&dest->wmr918WindBatteryStatus);
    tempshort = (USHORT*)&(dest->wmr918OutTempBatteryStatus);
    *tempshort = swapShortNTOH(&dest->wmr918OutTempBatteryStatus);
    tempshort = (USHORT*)&(dest->wmr918poolTempBatteryStatus);
    *tempshort = swapShortNTOH(&dest->wmr918poolTempBatteryStatus);
    tempshort = (USHORT*)&(dest->wmr918extra2BatteryStatus);
    *tempshort = swapShortNTOH(&dest->wmr918extra2BatteryStatus);

    return OK;
}

int datafeedConvertArchive_HTON(ARCHIVE_PKT_FP* dest, ARCHIVE_PKT* src)
{
    int         index;

    dest->dateTime              = htonl(src->dateTime);
    dest->usUnits               = htonl(src->usUnits);
    dest->interval              = htonl(src->interval);

    for (index = 0; index < DATA_INDEX_MAX; index ++)
    {
        dest->value[index]      = (ULONG)(src->value[index] * DF_FP_SHIFT);
        dest->value[index]      = htonl(dest->value[index]);
    }

    return OK;
}

int datafeedConvertArchive_NTOH(ARCHIVE_PKT* dest, ARCHIVE_PKT_FP* src)
{
    int         index;

    dest->dateTime              = ntohl(src->dateTime);
    dest->usUnits               = ntohl(src->usUnits);
    dest->interval              = ntohl(src->interval);

    for (index = 0; index < DATA_INDEX_MAX; index ++)
    {
        src->value[index]       = ntohl(src->value[index]);
        dest->value[index]      = (float)src->value[index]/DF_FP_SHIFT;
    }

    return OK;
}

